package com.fulihui.cloud.plugin;

/*
 * Copyright 2001-2005 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.fulihui.cloud.plugin.enums.DataBaseEnum;
import com.fulihui.cloud.plugin.model.ShardingRule;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Mojo(name = "generate", defaultPhase = LifecyclePhase.GENERATE_SOURCES, requiresDependencyResolution = ResolutionScope.TEST)
public class MyMojo
        extends AbstractMojo {
    @Parameter(property = "skip", defaultValue = "false")
    private boolean skip;

    @Parameter(property = "overwrite", defaultValue = "false")
    private boolean overwrite;

    @Parameter(property = "database", defaultValue = "MYSQL", required = true)
    private DataBaseEnum database;

    @Parameter(property = "createTableSqls", required = true)
    private Map<String, String> createTableSqls;

    @Parameter(property = "shardingRules", required = true)
    private List<String> shardingRules = Lists.newArrayList();

    public void execute() throws MojoExecutionException {
        if (skip) {
            getLog().info("sharding plugin skip ...");
            return;
        } else {
            //TODO
            //判断数据库是否符合
            if (Objects.nonNull(database)) {
                if (Objects.isNull(shardingRules) || shardingRules.isEmpty()) {
                    getLog().error("sharding rule error must not null");
                    return;
                }

                for (String t : shardingRules) {
                    String[] rule = t.split("::");
                    ShardingRule.ShardingRuleBuilder builder = new ShardingRule.ShardingRuleBuilder();
                    if (rule.length == 5) {
                        builder.addIp(rule[0])
                                .addPort(Integer.valueOf(rule[1]))
                                .addUserName(rule[2])
                                .addPassWord(rule[3])
                                .addDataBase(rule[4].split("-")[0])
                                .addDataBaseCount(Integer.valueOf(rule[4].split("-")[1].trim()))
                                //TODO
                                .addDriverName("com.mysql.jdbc.Driver");
                        //风险大暂时不执行
                        if (overwrite) {
                            //删库附带删表
                            dropDataBase(builder.builder());
                            //dropTable(builder.builder());
                        }

                        getLog().info(builder.builder().toString());

                        //创建数据库
                        createDataBase(builder.builder());
                    } else if (rule.length == 6) {
                        builder.addIp(rule[0])
                                .addPort(Integer.valueOf(rule[1]))
                                .addUserName(rule[2])
                                .addPassWord(rule[3])
                                .addDataBase(rule[4].split("-")[0])
                                .addDataBaseCount(Integer.valueOf(rule[4].split("-")[1].trim()))
                                // TODO
                                .addDriverName("com.mysql.jdbc.Driver");
                        Map<String, Integer> tables = Maps.newHashMap();
                        String[] tbs = rule[5].split(",");
                        for (String tb : tbs) {
                            tables.put(tb.split("-")[0], Integer.valueOf(tb.split("-")[1]));
                        }
                        builder.addTables(tables);
                        //风险大暂时不执行
                        if (overwrite) {
                            //删库附带删表
                            dropDataBase(builder.builder());
                            //dropTable(builder.builder());
                        }

                        getLog().info(builder.builder().toString());

                        //创建数据库
                        createDataBase(builder.builder());

                        //创建表
                        createTable(builder.builder());
                    }
                }
            }

            //后期扩充自动生成sharding-jdbc的配置
            //TODO
        }
    }

    public void dropDataBase(ShardingRule rule) {
        try {
            Class.forName(rule.getDriverName());
            // TODO
            Connection connection = DriverManager.getConnection("jdbc:mysql://" + rule.getIp() + ":" + rule.getPort(), rule.getUserName(), rule.getPassWord());
            Statement st = connection.createStatement();
            String preSql = "DROP DATABASE IF EXISTS ";
            if (rule.getDataBaseCount() == 0) {
                st.addBatch(preSql + rule.getDataBase());
            } else {
                for (int i = 0; i < rule.getDataBaseCount(); i++) {
                    st.addBatch(preSql + rule.getDataBase() + "_" + i);
                }
            }
            st.executeBatch();
            st.close();
        } catch (Exception e) {
            getLog().error(e);
        }
    }

    public void createDataBase(ShardingRule rule) {
        try {
            Class.forName(rule.getDriverName());
            // TODO
            Connection connection = DriverManager.getConnection("jdbc:mysql://" + rule.getIp() + ":" + rule.getPort(), rule.getUserName(), rule.getPassWord());
            Statement st = connection.createStatement();
            String preSql = "CREATE DATABASE IF NOT EXISTS ";
            String suffixSql = " DEFAULT CHARSET utf8 COLLATE utf8_general_ci";
            if (rule.getDataBaseCount() == 0) {
                st.addBatch(preSql + rule.getDataBase() + suffixSql);
            } else {
                for (int i = 0; i < rule.getDataBaseCount(); i++) {
                    st.addBatch(preSql + rule.getDataBase() + "_" + i + suffixSql);
                }
            }
            st.executeBatch();
            st.close();
        } catch (Exception e) {
            getLog().error(e);
        }
    }

    public void dropTable(ShardingRule rule) {
        try {
            Class.forName(rule.getDriverName());
            if (rule.getDataBaseCount() == 0) {
                // TODO
                Connection connection = DriverManager.getConnection("jdbc:mysql://" + rule.getIp() + ":" + rule.getPort() + "/" + rule.getDataBase(), rule.getUserName(), rule.getPassWord());
                Statement st = connection.createStatement();
                for (Map.Entry<String, Integer> tb : rule.getTables().entrySet()) {
                    for (int j = 0; j < tb.getValue(); j++) {
                        String sql = "DROP TABLE IF EXISTS " + tb.getKey() + "_" + j;
                        st.addBatch(sql);
                    }
                    st.addBatch("DROP TABLE IF EXISTS " + tb.getKey());
                }
                st.executeBatch();
                st.close();
            } else {
                // TODO
                for (int i = 0; i < rule.getDataBaseCount(); i++) {
                    Connection connection = DriverManager.getConnection("jdbc:mysql://" + rule.getIp() + ":" + rule.getPort() + "/" + rule.getDataBase() + "_" + i, rule.getUserName(), rule.getPassWord());
                    Statement st = connection.createStatement();
                    for (Map.Entry<String, Integer> tb : rule.getTables().entrySet()) {
                        for (int j = 0; j < tb.getValue(); j++) {
                            String sql = "DROP TABLE IF EXISTS " + tb.getKey() + "_" + j;
                            st.addBatch(sql);
                        }
                        st.addBatch("DROP TABLE IF EXISTS " + tb.getKey());
                    }
                    st.executeBatch();
                    st.close();
                }
            }
        } catch (Exception e) {
            getLog().error(e);
        }
    }

    public void createTable(ShardingRule rule) {
        try {
            Class.forName(rule.getDriverName());
            if (rule.getDataBaseCount() == 0) {
                // TODO
                Connection connection = DriverManager.getConnection("jdbc:mysql://" + rule.getIp() + ":" + rule.getPort() + "/" + rule.getDataBase(), rule.getUserName(), rule.getPassWord());
                Statement st = connection.createStatement();
                for (Map.Entry<String, Integer> tb : rule.getTables().entrySet()) {
                    String sql = createTableSqls.get(tb.getKey());
                    if (Objects.isNull(sql)) {
                        continue;
                    }
                    for (int j = 0; j < tb.getValue(); j++) {
                        st.addBatch(sql.replaceFirst(tb.getKey(), tb.getKey() + "_" + j).replace("TABLE", "").replace("CREATE", "CREATE TABLE IF NOT EXISTS"));
                    }
                    st.addBatch(sql.replace("TABLE", "").replace("CREATE", "CREATE TABLE IF NOT EXISTS"));
                }
                st.executeBatch();
                st.close();
            } else {
                // TODO
                for (int i = 0; i < rule.getDataBaseCount(); i++) {
                    Connection connection = DriverManager.getConnection("jdbc:mysql://" + rule.getIp() + ":" + rule.getPort() + "/" + rule.getDataBase() + "_" + i, rule.getUserName(), rule.getPassWord());
                    Statement st = connection.createStatement();
                    for (Map.Entry<String, Integer> tb : rule.getTables().entrySet()) {
                        String sql = createTableSqls.get(tb.getKey());
                        if (Objects.isNull(sql)) {
                            continue;
                        }
                        for (int j = 0; j < tb.getValue(); j++) {
                            st.addBatch(sql.replaceFirst(tb.getKey(), tb.getKey() + "_" + j).replace("TABLE", "").replace("CREATE", "CREATE TABLE IF NOT EXISTS"));
                        }
                        st.addBatch(sql.replace("TABLE", "").replace("CREATE", "CREATE TABLE IF NOT EXISTS"));
                    }
                    st.executeBatch();
                    st.close();
                }
            }
        } catch (Exception e) {
            getLog().error(e);
        }
    }
}
